package org.opens.util;


import cn.hutool.core.io.IoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;

/**
 * 功能描述
 *
 * @author by zhangyuming
 * @date 2022/9/22
 */
public class HttpUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * 默认的下载重试次数
     */
    private static final Integer DEFAULT_RE_TRY_NUM = 5;

    /**
     * 默认的下载重试等待时间
     */
    private static final Integer DEFAULT_RE_TRY_WAIT_TIME = 1000;

    /**
     * http最大连接等待时间
     */
    private static final Integer DEFAULT_CONNECT_WAIT_TIME = 30000;

    /**
     * http读取最大等待时间
     */
    private static final Integer DEFAULT_READ_WAIT_TIME = 30000;

    /**
     * 从url下载资源
     * @param url 下载第地址
     * @param desc 保存地址
     * @param reTryNum 重试次数
     * @param reTryWaitTime 重试等待时间
     * @param connectTimeOut 连接超时时间
     * @param readTimeOut 读取超时时间
     * @param downloadMode 下载的文件类型
     * @return 下载到的文件对象
     */
    public static File download(String url, String desc, int reTryNum, int reTryWaitTime, int connectTimeOut, int readTimeOut, DownloadMode downloadMode) {
        if (reTryNum < 1) {
            throw new IllegalArgumentException("重试次数必须大于0");
        }

        if (reTryWaitTime < 1) {
            throw new IllegalArgumentException("重试等待时间必须大于0");
        }

        // 如果目标地址是目录, 则使用uuid作为文件名
        File descFile = new File(desc);
        String downloadFileName = descFile.getName();
        if (descFile.isDirectory()) {
            if (!descFile.exists()) {
                boolean mkdirFlag = descFile.mkdirs();
                if (!mkdirFlag) {
                    logger.error("目标地址'{}'创建失败!", desc);
                    throw new RuntimeException("目标地址" + desc + "创建失败!");
                } else {
                    logger.error("目标地址'{}'创建成功!", desc);
                }
            }
            downloadFileName = UUID.randomUUID().toString() + (downloadMode == null ? "" : downloadMode.getSuffix());
            File downloadFile = new File(descFile, downloadFileName);
            desc = downloadFile.getAbsolutePath();
            logger.info("目标地址为目录类型, 自动设置文件名为{}", downloadFileName);
        }

        // 如果父目录不存在, 则自动创建
        if (!descFile.getParentFile().exists()) {
            boolean mkdirFlag = descFile.getParentFile().mkdirs();
            if (!mkdirFlag) {
                logger.error("目标地址父目录'{}'创建失败!", descFile.getParentFile().getAbsolutePath());
                throw new RuntimeException("目标地址父目录" + descFile.getParentFile().getAbsolutePath() + "创建失败!");
            } else {
                logger.info("目标地址父目录'{}'创建成功!", descFile.getParentFile().getAbsolutePath());
            }
        }

        // 防止网络延迟循环reTryNum次
        for (int j = 0; j < reTryNum; j++) {
            HttpURLConnection urlConnection = null;
            InputStream inputStream = null;
            FileOutputStream fileOutputStream = null;

            try {
                // 第一次进来不等待
                if (j != 0) {
                    Thread.sleep(reTryWaitTime);
                }

                // 创建URL对象
                URL urlObj = new URL(url);
                // 创建HttpURLConnection对象
                urlConnection = (HttpURLConnection) urlObj.openConnection();
                // 设置连接超时时间
                urlConnection.setConnectTimeout(connectTimeOut);
                // 设置读取超时时间
                urlConnection.setReadTimeout(readTimeOut);
                // 设置静默读取
                urlConnection.setDoInput(true);
                // 打开连接
                urlConnection.connect();
                // 获取输入流
                inputStream = urlConnection.getInputStream();

                // 创建输出流
                fileOutputStream = new FileOutputStream(desc);
                // 复制流内容
                IoUtil.copy(inputStream, fileOutputStream);

                return new File(desc);
            } catch (IOException e) {
                logger.error("下载文件错误: {}, 重试第{}次", e.getMessage(), j + 1);
                if (j == reTryNum - 1) {
                    logger.error("已达到最大下载重试次数, 仍旧下载失败!");
                    throw new RuntimeException("已达到最大下载重试次数, 仍旧下载失败!");
                }
            } catch (InterruptedException e) {
                logger.error("下载文件重试第{}次时线程打断!", j + 1);
                Thread.currentThread().interrupt();
            } finally {
                close(fileOutputStream);
                close(inputStream);
                close(urlConnection);
            }
        }

        return null;
    }

    /**
     * 默认的下载方法
     * @param url 下载地址
     * @param desc 保存路径
     * @return 保存的文件对象
     */
    public static File download(String url, String desc) {
        return download(url, desc, DEFAULT_RE_TRY_NUM, DEFAULT_RE_TRY_WAIT_TIME, DEFAULT_CONNECT_WAIT_TIME, DEFAULT_READ_WAIT_TIME, null);
    }

    /**
     * 默认的下载方法
     * @param url 下载地址
     * @param desc 保存路径
     * @param downloadMode 下载的文件类型
     * @return 保存的文件对象
     */
    public static File download(String url, String desc, DownloadMode downloadMode) {
        return download(url, desc, DEFAULT_RE_TRY_NUM, DEFAULT_RE_TRY_WAIT_TIME, DEFAULT_CONNECT_WAIT_TIME, DEFAULT_READ_WAIT_TIME, downloadMode);
    }

    /**
     * 统一关闭文件流或http连接
     * @param obj 待关闭的资源
     */
    private static void close(Object obj) {
        if (obj == null) {
            return;
        }

        if (obj instanceof HttpURLConnection) {
            try {
                ((HttpURLConnection) obj).disconnect();
            } catch (Exception e) {
                //
            }
        }

        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (Exception e) {
                //
            }
        }
    }

    public static enum DownloadMode {

        JPG(".jpg"),
        PNG(".png"),
        GIF(".gif");

        private String suffix;

        DownloadMode(String suffix) {
            this.suffix = suffix;
        }

        public String getSuffix() {
            return suffix;
        }
    }

}
